package com.ia.faceDetectetion;


import hypermedia.video.OpenCV;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Label;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.MemoryImageSource;
import java.awt.image.RescaleOp;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import processing.core.PApplet;

import com.ia.faceDetectetion.downloadMosaicImage;
import com.ia.faceDetectetion.SimpleTTS;
import com.ia.neuralNetwork.*;
import processing.core.*; 
import processing.xml.*; 

public class FaceDetection extends JFrame implements Runnable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	// program execution frame rate (millisecond)
	final int FRAME_RATE = 500 / 30;

	final static int ALTO_REJILLA = 20;
	final static int ANCHO_REJILLA = 20;

	final static int MODO_NOTHING = -1;
	final static int MODO_RECONOCER = 1;
	final static int MODO_APRENDER = 2;

	public static int  distanciaCara =0;

	public static boolean voz = false;
	public static int BINA = 400;
	public static int iteraciones = 0;
	public static int contadorCaras = 0;
	
	public static int maxIteraciones = 15;

	final static String FICHERO_PATRONES = "/Volumes/programacion/redneuronal/patrones/CARAS.TXT";
	final static String FICHERO_PESOS = "WCARAS.TXT";
	final static String PATH_PESOS = "/Volumes/programacion/redneuronal/pesos/";

	public static String resultadoAnterior =null;
	public static neuralNetwork nn = null;
	public static downloadMosaicImage dmi = null;

	public static String cara = null;
	public static String caraAnterior = "";
	
	public static RescaleOp rescale;
	public static String[][] caras = null;

	public static int CONTRASTE = 0;
	public static int BRILLO = 0;

	public static int modo = MODO_NOTHING;

	private static BufferedImage imagen;

	static Label labelMensaje;

	public static JButton bAprender;
	public static JButton bSalir;

	static OpenCV cv = null; // OpenCV Object
	static Thread t = null; // the sample thread
	static BufferedImage g1;

	Image image;

	static int contador = 0;

	// the input video stream image
	Image frame = null;
	// list of all face detected area
	Rectangle[] squares = new Rectangle[0];

	/**
	 * Setup Frame and Object(s).
	 */
	FaceDetection() {

		super("");

		inicializaRed();

		// cv.stop();

		caras = new String[3][2];

		caras[0][0] = "0011";
		caras[0][1] = "Sami";

		caras[1][0] = "0111";
		caras[1][1] = "Montse";

		caras[2][0] = "1111";
		caras[2][1] = "Pol";

		// frame setup
		// this.setBounds(50, 50, cv.width, cv.height);
		this.setBackground(Color.LIGHT_GRAY);
		this.setVisible(true);

		setSize(350, 340);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//setLocation(800,0);
		
		setLocationRelativeTo(null);

		setResizable(false);

		// OpenCV setup
		cv = new OpenCV();

		cv.capture(350, 240);
		cv.cascade(OpenCV.CASCADE_FRONTALFACE_ALT);

		getContentPane().setLayout(null);

		bAprender = new JButton("Aprender");
		bAprender.setSize(new java.awt.Dimension(110, 41));
		bAprender.setVisible(true);
		bAprender.setText("Aprender");
		bAprender.setEnabled(false);

		bSalir = new JButton("Salir");
		bSalir.setSize(new java.awt.Dimension(110, 41));
		bSalir.setVisible(true);
		bSalir.setText("Salir");

		bSalir.setLocation(new java.awt.Point(115, 255));
		bAprender.setLocation(new java.awt.Point(1, 255));

		labelMensaje = new Label();
		labelMensaje.setText("");
		labelMensaje.setLocation(2, 230);
		labelMensaje.setSize(200, 5);

		getContentPane().add(bAprender);
		getContentPane().add(bSalir);

		getContentPane().add(labelMensaje);

		getContentPane().repaint();

		// Se le dice al botÛn quÈ tiene que hacer cuando lo pulsemos.
		bAprender.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				contador = 0;
				voz = false;
				caraAnterior="";
				modo = MODO_APRENDER;
				
			}
		});

		bSalir.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				//String email    = JOptionPane.showInputDialog("Direccion email (ej. marioly@gmail.com", "algo@gmail.com");
				System.exit(0);
			}
		});

		// start running program
		t = new Thread(this);
		t.start();

	}

	public static BufferedImage binariza(Image im) {

		BufferedImage reticula = toBufferedImage(im);

		int argb, r, bg, g;

		int colorBlanco = 0;
		int colorNegro = 0;

		for (int x = 0; x < reticula.getWidth(); x = x + 1) {
			for (int y = 0; y < reticula.getHeight(); y = y + 1) {

				argb = reticula.getRGB(x, y);
				r = 0xff & (argb >> 16);
				g = 0xff & (argb >> 8);
				bg = 0xff & argb;

				int z = r + g + bg;

				if (z > BINA) {
					Color color1 = new Color(255, 255, 255);
					reticula.setRGB(x, y, color1.getRGB());

					colorBlanco++;
				} else {

					colorNegro++;
					Color color2 = new Color(0, 0, 0);
					reticula.setRGB(x, y, color2.getRGB());
				}
			}
			// s=s+"\n";
		}

		int t = colorNegro + colorBlanco;

		int ne = (colorNegro * 100) / t;
		int ba = (colorBlanco * 100) / t;

		if (ba >= 80) {
			BINA = BINA + 5;
		} else {
			if (ne>20) {
				BINA = BINA - 5;
			}
		}

		int d = (int) Math.sqrt(ba);

	//	System.out.println(ba+"------->"+iteraciones);

		if (d == 8 && modo != MODO_APRENDER) {
			iteraciones++;
		}

		if (iteraciones > maxIteraciones) {

			bAprender.setEnabled(true);
			//bAprender.repaint();
			iteraciones = 0;
			modo = MODO_RECONOCER;
		}

		// System.out.println("Negro =" + ne + "%   " + "Blanco =" + ba + "%"
		// + "  " + BINA);

		return reticula;

	}

	public static BufferedImage toBufferedImage(Image image) {
		/** miramos uqe la imagen no sea ya una instancia de BufferedImage */
		if (image instanceof BufferedImage) {
			/** genial, no hay que hacer nada */
			return ((BufferedImage) image);
		} else {
			/** nos aseguramos que la imagen está totalmente cargada */
			image = new ImageIcon(image).getImage();
			/** creamos la nueva imagen */
			BufferedImage bufferedImage = new BufferedImage(image
					.getWidth(null), image.getHeight(null),
					BufferedImage.TYPE_USHORT_GRAY);
			Graphics g = bufferedImage.createGraphics();
			g.drawImage(image, 0, 0, null);
			g.dispose();
			return (bufferedImage);
		}
	}

	public static BufferedImage scaleToSize(int nMaxWidth, int nMaxHeight,
			BufferedImage imgSrc) {
		int nHeight = imgSrc.getHeight();
		int nWidth = imgSrc.getWidth();
		double scaleX = (double) nMaxWidth / (double) nWidth;
		double scaleY = (double) nMaxHeight / (double) nHeight;
		double fScale = Math.min(scaleX, scaleY);
		return scale(fScale, imgSrc);
	}

	public static BufferedImage scale(double scale, BufferedImage srcImg) {
		if (scale == 1) {
			return srcImg;
		}
		AffineTransformOp op = new AffineTransformOp(AffineTransform
				.getScaleInstance(scale, scale), null);

		return op.filter(srcImg, null);

	}

	public void paint(Graphics g) {

		// draw image
		g.drawImage(frame, 0, 0, null);
		BufferedImage img = null;
		String icono = null;
		
		
		if (squares.length == 0) {
			iteraciones = 0;
			cara = null;
			voz = false;
			icono = null;
			FaceDetection.this.setTitle("");
			contadorCaras=0;
		}
		
		double newX =0;
		double newY = 0;
		
		for (int i = 0; i < squares.length; i++) {

			int ancho = squares[i].getSize().width;
			int alto = squares[i].getSize().height;
			int area = ancho * alto;
			
			distanciaCara = (int) Math.sqrt(area);

			if (distanciaCara >= 80 && distanciaCara <= 110) {

				icono = "/Volumes/programacion/verde.gif";

     			bAprender.setEnabled(true);
//				bAprender.repaint();

				g1 = utilidades.toBufferedImage(frame);

				g1 = g1.getSubimage(squares[i].x + 10, squares[i].y,
						squares[i].width - 30, squares[i].height - 10);

				g1 = binariza(g1);
				g1 = scaleToSize(20, 20, g1);

				 g.drawImage(g1, 20, 20, null);

				if (modo == MODO_RECONOCER || modo == MODO_APRENDER) {

					if (modo == MODO_APRENDER) {

						cara = null;

						g.setColor(Color.RED);
						g.drawRect(squares[i].x + 10, squares[i].y,
								squares[i].width - 30, squares[i].height - 10);
					}

					try {
						loadImage();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				}

			} else {
     			bAprender.setEnabled(false);
//				bAprender.repaint();
				icono = "/Volumes/programacion/roja.gif";
			}
		}

		if (icono != null) {
			try {
				img = ImageIO.read(new File(icono));
				g.drawImage(img, (int) newX, (int) newY+20, null);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
	}

	public String loadImage() throws Exception {

		String s = "";

		imagen = g1;

		// String file = "/cara" + String.valueOf(contador) + ".png";

		dmi = new downloadMosaicImage(imagen, ANCHO_REJILLA, ALTO_REJILLA);
		s = dmi.imagenString;
		
		if (modo == MODO_RECONOCER) {
			
			String resultado=null;
			
			
			if (contadorCaras<5) {
				resultado = nn.pruebaRedModelo(s);
			} else {
				resultado=resultadoAnterior;
			}
			
			if ( resultadoAnterior!=null) {
				if ( !resultadoAnterior.equals(resultado)) {
					FaceDetection.this.setTitle("");
					voz = true;
					caraAnterior="";
					cara=null;
					iteraciones = 0;
					contadorCaras=0;
				}
				
				if ( resultadoAnterior.equals(resultado)) {
					contadorCaras++;
				}
			}
			
			
			
			System.out.println("cara = "+resultado+" anterior "+resultadoAnterior+ "   "+contadorCaras);
			
		    resultadoAnterior = resultado;
			
			cara = null;
			
			for (int z = 0; z < caras.length; z++) {
				if (resultado.equals(caras[z][0])) {
					cara = caras[z][1];

					break;
				}
			}
			
			if ( resultadoAnterior!=null && cara!=null) {
				if ( resultadoAnterior.equals(resultado)) {
					FaceDetection.this.setTitle(cara);
				}
			}
			
			// SimpleTTS voz = null;
			// voz = new SimpleTTS("kevin16");

			if (!voz) {
				if ( cara!=null) {
					
					if ( !caraAnterior.equals(cara)) {
						speak("hola !," + cara);
					}
					
					voz = true;
					iteraciones = 0;
				} else {
					iteraciones = 0;
					speak("no te reconozco ");
					cara = null;
					FaceDetection.this.setTitle("");
					iteraciones = 0;
					caraAnterior="";
					voz = true;
				}
			}
			
			caraAnterior=cara;
			modo = MODO_NOTHING;
			cara = null;
		}

		if (modo == MODO_APRENDER) {
			iteraciones = 0;
			s = s + "0;0;1;1;";

			if (nn.pattern.isPatternValid(s) == "") {
				contador++;

				if (contador < 10) {
					labelMensaje.setText("Capturando...");
					nn.pattern.savePattern("", FICHERO_PATRONES, s);
				} else {
					modo = MODO_NOTHING;
					// cv.stop();
					aprende();

				}
			}
		}

		return (s);

	}

	/**
	 * Execute this sample.
	 */
	public void run() {
		while (t != null && cv != null) {
			try {
				Thread.sleep(FRAME_RATE);

				// grab image from video stream
				cv.read();

				cv.blur(OpenCV.BLUR, 1);

				cv.contrast(CONTRASTE);
				cv.brightness(BRILLO);

				// create a new image from cv pixels data
				MemoryImageSource mis = new MemoryImageSource(cv.width,
						cv.height, cv.pixels(), 0, cv.width);

				frame = createImage(mis);

				// detect faces
				squares = cv.detect(1.2f, 1, OpenCV.HAAR_DO_CANNY_PRUNING, 20,
						20);

				// squares = cv.detect();

				// of course, repaint
				repaint();
			} catch (InterruptedException e) {
				;
			}
		}
	}

	public static void inicializaRed() {

		nn = new neuralNetwork(ALTO_REJILLA*ANCHO_REJILLA, 200, 4, FICHERO_PATRONES);
		nn.setCoficienteAprendizaje((float) 0.1);
		nn.weight.loadWeight(PATH_PESOS,FICHERO_PESOS);

	}

	public static void aprende() throws FileNotFoundException, IOException,
			Exception {

		inicializaRed();
		labelMensaje.setText("Aprendiendo...");

		while (!nn.isfinishedLearning()) {
			// labelPrintError.setText("");
			nn.entrenaRed();
		}

		labelMensaje.setText("");
		nn.weight.saveWeights(PATH_PESOS,FICHERO_PESOS);

		
		//speak("aprendido");
		cara = null;
		iteraciones = 0;
		voz = false;
		modo = MODO_RECONOCER;

	}

	public static void speak__(String texto) throws Exception {
		SimpleTTS voz = null;
		voz = new SimpleTTS("kevin16");
		voz.speak(texto);
		voz.close();
		voz = null;

	}
	
	public static void speak(String texto) {

		String a1 = texto;
		String a2 = "Cepstral miguel";

		String script = "tell current application\n say \"" + a1 + "\""
				+ " using \"" + a2 + "\"" + "\n" + "end tell";

		ScriptEngineManager mgr = new ScriptEngineManager();
		ScriptEngine engine = mgr.getEngineByName("AppleScript");
		ScriptContext context = engine.getContext();
		try {
			engine.eval(script, context);
		} catch (ScriptException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		mgr = null;
		engine = null;
		context = null;
	}

	public static void main(String[] args) throws FileNotFoundException,
			IOException {
		
		new FaceDetection();

	}

}